{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Multiple dispatch"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Multiple dispatch** is a key feature of Julia, that we will explore in this notebook.\n",
    "\n",
    "It helps make software fast. It also makes software extensible, programmable, and downright fun to play with. \n",
    "\n",
    "It may just herald a breakthrough for parallel computation."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "1. Roman numerals\n",
    "2. Functions\n",
    "3. Parallel computing"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 1. Roman numerals (for fun)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Let's define a **new struct** that represents a Roman numeral. For coding simplicity, we'll just deal with numbers between 0 and 9. \n",
    "\n",
    "**Exercise**: Extend this to larger numbers. (Recall that Roman numbers are a base-10 system!)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "struct Roman\n",
    "    n::Int\n",
    "end\n",
    "\n",
    "Base.show(io::IO, r::Roman) = print(io, 'ⅰ' + (r.n - 1) % 10)  # nice display; 'ⅰ' is a Unicode Roman numeral"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We can create an object of this type as follows:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "Roman(4)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "typeof.([5 5.0 Roman(5) \"Five\" '5'  5//1])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We would like to display it nicely, in Roman numerals:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "x = [7 1 2 5 8 9]\n",
    "Roman.(x)   # equivalent to map(Roman, x)  or  [Roman(w) for w in x]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "It'd be nice to be able to add Roman numerals together like normal numbers:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "Roman(4) + Roman(5)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "But Julia doesn't know how to do that. Let's teach it by `import`ing the `+` function, which then allows us to _extend_ its definition:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import Base: +, *\n",
    "\n",
    "+(a::Roman, b::Roman) = Roman(a.n + b.n)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "Roman(4) + Roman(5)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "This **adds a new method** to the function `+`:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "methods(+)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import Base.*\n",
    "*(i::Roman, j::Roman) = Roman(i.n * j.n)                     # Multiply like a Roman"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "Roman(3) * Roman(2)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "Roman.(1:3) .* [Roman(1) Roman(2) Roman(3)]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "But "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "Roman(3) * 2"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Complicated mytimes to decide what to do based on type\n",
    "# not suggested, better way coming soon\n",
    "function mytimes(i,j)\n",
    "  if isa(i,Roman) & isa(j,Number)\n",
    "        return  fill(1, i.n, j)   # i by j matrix with ones\n",
    "    elseif    isa(i,Number) & isa(j,Roman) \n",
    "        return \"😄\"^(i*j.n)   #  i * j happy faces\n",
    "    else\n",
    "        return(\"I Don't know\")\n",
    "    end\n",
    "end"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "mytimes(4,Roman(3)) # Twelve happys"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "mytimes(Roman(4),3) # 4x3 matrix with ones"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The simplest thing to do is to explicitly define multiplication of a `Roman` by a number. We can do it as we see fit:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "*(i::Number, j::Roman) = \"😄\"^(i*j.n)        #  i * j happy faces\n",
    "\n",
    "*(i::Roman, j::Number) = fill(1, i.n, j)       # i by j matrix"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "3 * Roman(3) # Nine happys"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "Roman(3) * 5  # Three by Five matrix of ones"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "t(x::Roman,y::Roman) = x.n * y.n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "t(Roman(5),Roman(4))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Notice how tight the assembler is!\n",
    "@code_native t(Roman(2),Roman(4))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Functions"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import Base: *, +, ^"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "*(α::Number,   g::Function) = x -> α * g(x)   # Scalar times function\n",
    "\n",
    "*(f::Function, λ::Number)   = x -> f(λ * x)   # Scale the argument\n",
    "\n",
    "*(f::Function, g::Function) = x -> f(g(x))    # Function composition  -- abuse of notation!  use \\circ in Julia 0.6\n",
    "\n",
    "^(f::Function, n::Integer) = n == 1 ? f : f*f^(n-1) # A naive exponentiation algorithm by recursive multiplication"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "+(f::Function, g::Function) = x -> f(x) + g(x)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "For example, the exponential function is defined as\n",
    "\n",
    "$$\\exp(x) = \\sum_{n=0}^\\infty \\frac{1}{n!} x^n.$$\n",
    "\n",
    "We can think of this just in terms of functions:\n",
    "\n",
    "$$\\exp = \\sum_{n=0}^\\infty \\frac{1}{n!} \\mathrm{pow}_n,$$\n",
    "\n",
    "where $\\mathrm{pow}_n(x) = x^n$.\n",
    "\n",
    "(starts to blur the symbolic with the numerical!)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "pow(n) = x -> x^n\n",
    "\n",
    "myexp = sum(1/factorial(big(n)) * pow(n) for n in 0:100)   # taylor series not efficient!"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "[myexp(1); exp(1); exp(big(1))]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "f = x -> x^2\n",
    "f(10)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "g = 3f\n",
    "g(10)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "(f^2)(10)  # since we defined multiplication of functions as composition"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "using Plots;\n",
    "gr()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "x = pi*(0:0.001:4)\n",
    "\n",
    "plot(x, sin.(x),    c=\"black\", label=\"Fun\")\n",
    "plot!(x, (12*sin).(x),    c=\"green\", label=\"Num * Fun\")\n",
    "plot!(x, (sin*12).(x),    c=\"red\", alpha=0.9, label=\"Fun * Num\")\n",
    "plot!(x, (5*sin*exp).(x), c=\"blue\", alpha=0.2, label=\"Num * Fun * Fun\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "plot([12*sin, sin*12, 5*sin*exp], 0:.01:4π, α=[1 .9 .2], c=[:green :red :blue])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<img src=\"https://lh4.googleusercontent.com/--z5eKJbB7sg/UffjL1iAd4I/AAAAAAAABOc/S_wDVyDOBfQ/gauss.jpg\">"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "###  \"Sin^2 phi is odious to me, even though Laplace made use of it; should  it be feared that sin^2 phi might become ambiguous, which would perhaps  never occur, or at most very rarely when speaking of sin(phi^2), well  then, let us write (sin phi)^2, but not sin^2 phi, which by analogy  should signify sin(sin phi).\" -- Gauss"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "x=(0:.01:2) * pi;\n",
    "\n",
    "plot(x, (sin^2).(x), c=\"blue\")     # Squaring just works, y=sin(sin(x)), Gauss would be pleased!\n",
    "plot!(x, sin.(x).^2,  c=\"red\")         "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Exercise"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "h(a, b::Any) = \"fallback\"\n",
    "h(a::Number, b::Number) = \"a and b are both numbers\"\n",
    "h(a::Number, b) = \"a is a number\"\n",
    "h(a, b::Number) = \"b is a number\"\n",
    "h(a::Integer, b::Integer) = \"a and b are both integers\""
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Try playing with h"
   ]
  }
 ],
 "metadata": {
  "anaconda-cloud": {},
  "kernelspec": {
   "display_name": "Julia 1.0.0",
   "language": "julia",
   "name": "julia-1.0"
  },
  "language": "Julia",
  "language_info": {
   "file_extension": ".jl",
   "mimetype": "application/julia",
   "name": "julia",
   "version": "1.0.0"
  },
  "toc": {
   "nav_menu": {
    "height": "119px",
    "width": "251px"
   },
   "navigate_menu": true,
   "number_sections": true,
   "sideBar": true,
   "threshold": "2",
   "toc_cell": false,
   "toc_section_display": "block",
   "toc_window_display": false
  }
 },
 "nbformat": 4,
 "nbformat_minor": 1
}
